package org.jet.emall.log;

import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jet.emall.common.constant.CommonMapKey;
import org.jet.emall.rbac.dto.AdminOpLogDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * @author xiaozai
 * @version 1.0
 * @date 2020-04-14 14:54
 */
@Aspect
@Component
public class OpLogAspect {

    @Autowired
    private ThreadPoolUtil threadPoolUtil;

    private Logger log = LoggerFactory.getLogger(getClass());

    /**
     *  这个切点切在所有需要打操作日志的控制器方法上，主要就是增，改，删操作方法上
     */
    @Pointcut(value = "@annotation(org.jet.emall.log.AdminOperationLog)")
    public void cutService() {
    }

    /**
     *  以环切方法
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {

        //先执行业务
        Object result = point.proceed();

        try {
            handle(point,result);
        } catch (Exception e) {
            log.error("日志记录出错!", e);
        }

        return result;
    }

    private void handle(ProceedingJoinPoint point, Object result) throws Exception {
        long beginTime = System.currentTimeMillis();
        LocalDateTime beginDateTime = LocalDateTime.now();
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //获取拦截的方法名
        Signature sig = point.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        msig = (MethodSignature) sig;
        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodName = currentMethod.getName();


        Integer userId = (Integer) request.getAttribute(CommonMapKey.USER_ID_KEY);
        String username = (String) request.getAttribute(CommonMapKey.USER_NAME_KEY);
        Integer roleId = (Integer) request.getAttribute(CommonMapKey.ROLE_ID_KEY);
        String roleName = (String) request.getAttribute(CommonMapKey.ROLE_NAME_KEY);
       if(userId==null||username==null||roleId==null||roleName==null){
           return;
       }

        //获取类名
        String className = point.getTarget().getClass().getName();
        //获取参数
        Object[] params = point.getArgs();

        //获取操作名称
        AdminOperationLog annotation = currentMethod.getAnnotation(AdminOperationLog.class);
        String func = annotation.value();

        StringBuilder sb = new StringBuilder();
        for (Object param : params) {
            sb.append(param);
            sb.append(" & ");
        }
        String args = sb.toString();
        LogType logType = annotation.logType();


        String reqUrl = request.getRequestURL().toString();
        String ip = request.getRemoteAddr();
        String userAgent = request.getHeader("User-Agent");
        ApiOperation apiOperation = currentMethod.getAnnotation(ApiOperation.class);
        String menu = apiOperation.notes().substring(apiOperation.notes().indexOf(":") + 1);
        String returnResult = result.toString();
        long spendTime = System.currentTimeMillis() - beginTime;
        AdminOpLogDTO operationLog = LogFactory.createOperationLog(userId, username, roleId, roleName, logType, className, methodName, args, reqUrl, func, OpResultType.SUCCESS, returnResult, menu, ip, userAgent, (int)spendTime,beginDateTime);
        threadPoolUtil.addTask(operationLog);
    }

}
